package com.gitee.loyo.jdbc

import com.gitee.loyo.KSQL
import com.gitee.loyo.KSQL.formatter
import java.io.InputStream
import java.io.Reader
import java.lang.RuntimeException
import java.math.BigDecimal
import java.net.URL
import java.sql.*
import java.sql.Array
import java.sql.Date
import java.util.*
import kotlin.collections.ArrayList

class PStmt(private val stmt: PreparedStatement, private val sql: String): PreparedStatement by stmt{
    private val paramMsg = ArrayList<String>()
    private var closeListener: (() -> Unit)? = null
    private var alreadyLogSQL = false

    fun handleParameters(conn:Conn, vararg args: Any?){
        for (i in args.indices) {
            val arg = args[i]
            if (arg == null) {
                this.setNull(i + 1, Types.NULL)
            } else {
                JDBC.parameterHandlerMap[arg::class]
                    ?.also {
                        (it as ParameterHandler<Any>).handle(this, i + 1, arg)
                    }
                    ?: throw RuntimeException("未找到" + arg.javaClass.name + ", 请使用JDBC.putParameterHandler添加相应类型处理")
            }
            JDBCListener.onParameterizedListener?.invoke(conn, this, i + 1, arg)
        }
    }

    fun setOnCloseListener(closeListener: () -> Unit): PStmt {
        this.closeListener = closeListener
        return this
    }

    private fun printParameters(){
        if(!alreadyLogSQL){
            KSQL.log("<== {}", sql)
            alreadyLogSQL = true
        }
        if(paramMsg.isEmpty()){
            KSQL.log("<== None Parameter")
        }else{
            KSQL.log("<== " + paramMsg.joinToString(", "))
        }
        paramMsg.clear()
    }

    override fun executeQuery(sql: String?): ResultSet {
        printParameters()
        return stmt.executeQuery(sql)
    }

    override fun executeQuery(): ResultSet {
        printParameters()
        return stmt.executeQuery()
    }

    override fun executeUpdate(sql: String?, columnIndexes: IntArray?): Int {
        printParameters()
        return stmt.executeUpdate(sql, columnIndexes).also { KSQL.log("==> {}", it) }
    }

    override fun executeUpdate(sql: String?, columnNames: kotlin.Array<out String>?): Int {
        printParameters()
        return stmt.executeUpdate(sql, columnNames).also { KSQL.log("==> {}", it) }
    }

    override fun executeUpdate(): Int {
        printParameters()
        return stmt.executeUpdate().also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeUpdate(sql: String?): Int {
        printParameters()
        return stmt.executeUpdate(sql).also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeUpdate(sql: String?, autoGeneratedKeys: Int): Int {
        printParameters()
        return stmt.executeUpdate(sql, autoGeneratedKeys).also { KSQL.log("==> {}", it) }
    }

    override fun execute(sql: String?, columnNames: kotlin.Array<out String>?): Boolean {
        printParameters()
        return stmt.execute(sql, columnNames)
    }

    override fun execute(sql: String?): Boolean {
        printParameters()
        return stmt.execute(sql)
    }

    override fun execute(sql: String?, columnIndexes: IntArray?): Boolean {
        printParameters()
        return stmt.execute(sql, columnIndexes)
    }

    override fun execute(sql: String?, autoGeneratedKeys: Int): Boolean {
        printParameters()
        return stmt.execute(sql, autoGeneratedKeys)
    }

    override fun execute(): Boolean {
        printParameters()
        return stmt.execute()
    }

    override fun addBatch() {
        printParameters()
        stmt.addBatch()
    }

    override fun executeBatch(): IntArray {
        return stmt.executeBatch().also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeLargeBatch(): LongArray {
        return stmt.executeLargeBatch().also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeLargeUpdate(sql: String?): Long {
        printParameters()
        return stmt.executeLargeUpdate(sql).also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeLargeUpdate(sql: String?, autoGeneratedKeys: Int): Long {
        printParameters()
        return stmt.executeLargeUpdate(sql, autoGeneratedKeys).also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeLargeUpdate(sql: String?, columnIndexes: IntArray?): Long {
        printParameters()
        return stmt.executeLargeUpdate(sql, columnIndexes).also {
            KSQL.log("==> {}", it)
        }
    }

    override fun executeLargeUpdate(sql: String?, columnNames: kotlin.Array<out String>?): Long {
        printParameters()
        return stmt.executeLargeUpdate(sql, columnNames)
    }

    override fun setNull(parameterIndex: Int, sqlType: Int) {
        stmt.setNull(parameterIndex, sqlType)
        paramMsg.add("null")
    }

    override fun setNull(parameterIndex: Int, sqlType: Int, typeName: String?) {
        stmt.setNull(parameterIndex, sqlType, typeName)
        paramMsg.add("null")
    }

    override fun setBoolean(parameterIndex: Int, x: Boolean) {
        stmt.setBoolean(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Boolean)")
    }

    override fun setByte(parameterIndex: Int, x: Byte) {
        stmt.setByte(parameterIndex, x)
        paramMsg.add("${Integer.toHexString(x.toInt())}(Byte)")
    }

    override fun setShort(parameterIndex: Int, x: Short) {
        stmt.setShort(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Short)")
    }

    override fun setInt(parameterIndex: Int, x: Int) {
        stmt.setInt(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Int)")
    }

    override fun setLong(parameterIndex: Int, x: Long) {
        stmt.setLong(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Long)")
    }

    override fun setFloat(parameterIndex: Int, x: Float) {
        stmt.setFloat(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Float)")
    }

    override fun setDouble(parameterIndex: Int, x: Double) {
        stmt.setDouble(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Double)")
    }

    override fun setBigDecimal(parameterIndex: Int, x: BigDecimal) {
        stmt.setBigDecimal(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(BigDecimal)")
    }

    override fun setString(parameterIndex: Int, x: String) {
        stmt.setString(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(String)")
    }

    override fun setBytes(parameterIndex: Int, x: ByteArray) {
        stmt.setBytes(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(ByteArray)")
    }

    override fun setDate(parameterIndex: Int, x: Date, cal: Calendar) {
        stmt.setDate(parameterIndex, x, cal)
        paramMsg.add("${formatter.toString(x)}(Date)")
    }

    override fun setDate(parameterIndex: Int, x: Date) {
        stmt.setDate(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Date)")
    }

    override fun setTime(parameterIndex: Int, x: Time, cal: Calendar) {
        stmt.setTime(parameterIndex, x, cal)
        paramMsg.add("${formatter.toString(x)}(Time)")
    }

    override fun setTime(parameterIndex: Int, x: Time) {
        stmt.setTime(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Time)")
    }

    override fun setTimestamp(parameterIndex: Int, x: Timestamp) {
        stmt.setTimestamp(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Timestamp)")
    }

    override fun setTimestamp(parameterIndex: Int, x: Timestamp, cal: Calendar) {
        stmt.setTimestamp(parameterIndex, x, cal)
        paramMsg.add("${formatter.toString(x)}(Timestamp)")
    }

    override fun setAsciiStream(parameterIndex: Int, x: InputStream, length: Int) {
        stmt.setAsciiStream(parameterIndex, x, length)
        paramMsg.add("${formatter.toString(x)}(AsciiStream)")
    }

    override fun setAsciiStream(parameterIndex: Int, x: InputStream, length: Long) {
        stmt.setAsciiStream(parameterIndex, x, length)
        paramMsg.add("${formatter.toString(x)}(AsciiStream)")
    }

    override fun setAsciiStream(parameterIndex: Int, x: InputStream) {
        stmt.setAsciiStream(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(AsciiStream)")
    }

    override fun setUnicodeStream(parameterIndex: Int, x: InputStream, length: Int) {
        stmt.setUnicodeStream(parameterIndex, x, length)
        paramMsg.add("${formatter.toString(x)}(UnicodeStream)")
    }

    override fun setBinaryStream(parameterIndex: Int, x: InputStream, length: Long) {
        stmt.setBinaryStream(parameterIndex, x, length)
        paramMsg.add("${formatter.toString(x)}(BinaryStream)")
    }

    override fun setBinaryStream(parameterIndex: Int, x: InputStream) {
        stmt.setBinaryStream(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(BinaryStream)")
    }

    override fun setBinaryStream(parameterIndex: Int, x: InputStream, length: Int) {
        stmt.setBinaryStream(parameterIndex, x, length)
        paramMsg.add("${formatter.toString(x)}(BinaryStream)")
    }

    override fun setObject(parameterIndex: Int, x: Any, targetSqlType: Int, scaleOrLength: Int) {
        stmt.setObject(parameterIndex, x, targetSqlType, scaleOrLength)
        paramMsg.add("${formatter.toString(x)}(Object)")
    }

    override fun setObject(parameterIndex: Int, x: Any) {
        stmt.setObject(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Object)")
    }

    override fun setObject(parameterIndex: Int, x: Any, targetSqlType: Int) {
        stmt.setObject(parameterIndex, x, targetSqlType)
        paramMsg.add("${formatter.toString(x)}(Object)")
    }

    override fun setObject(parameterIndex: Int, x: Any, targetSqlType: SQLType, scaleOrLength: Int) {
        stmt.setObject(parameterIndex, x, targetSqlType, scaleOrLength)
        paramMsg.add("${formatter.toString(x)}(Object)")
    }

    override fun setObject(parameterIndex: Int, x: Any, targetSqlType: SQLType) {
        stmt.setObject(parameterIndex, x, targetSqlType)
        paramMsg.add("${formatter.toString(x)}(Object)")
    }

    override fun setCharacterStream(parameterIndex: Int, reader: Reader, length: Int) {
        stmt.setCharacterStream(parameterIndex, reader, length)
        paramMsg.add("${formatter.toString(reader)}(CharacterStream)")
    }

    override fun setCharacterStream(parameterIndex: Int, reader: Reader) {
        stmt.setCharacterStream(parameterIndex, reader)
        paramMsg.add("${formatter.toString(reader)}(CharacterStream)")
    }

    override fun setCharacterStream(parameterIndex: Int, reader: Reader, length: Long) {
        stmt.setCharacterStream(parameterIndex, reader, length)
        paramMsg.add("${formatter.toString(reader)}(CharacterStream)")
    }

    override fun setRef(parameterIndex: Int, x: Ref) {
        stmt.setRef(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Ref)")
    }

    override fun setBlob(parameterIndex: Int, inputStream: InputStream, length: Long) {
        stmt.setBlob(parameterIndex, inputStream, length)
        paramMsg.add("${formatter.toString(inputStream)}(Blob)")
    }

    override fun setBlob(parameterIndex: Int, inputStream: InputStream) {
        stmt.setBlob(parameterIndex, inputStream)
        paramMsg.add("${formatter.toString(inputStream)}(Blob)")
    }

    override fun setBlob(parameterIndex: Int, x: Blob) {
        stmt.setBlob(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Blob)")
    }

    override fun setClob(parameterIndex: Int, reader: Reader) {
        stmt.setClob(parameterIndex, reader)
        paramMsg.add("${formatter.toString(reader)}(Clob)")
    }

    override fun setClob(parameterIndex: Int, x: Clob) {
        stmt.setClob(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Clob)")
    }

    override fun setClob(parameterIndex: Int, reader: Reader, length: Long) {
        stmt.setClob(parameterIndex, reader, length)
        paramMsg.add("${formatter.toString(reader)}(Clob)")
    }

    override fun setArray(parameterIndex: Int, x: Array) {
        stmt.setArray(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(Array)")
    }

    override fun setURL(parameterIndex: Int, x: URL) {
        stmt.setURL(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(URL)")
    }

    override fun setRowId(parameterIndex: Int, x: RowId) {
        stmt.setRowId(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(RowId)")
    }

    override fun setNString(parameterIndex: Int, x: String) {
        stmt.setNString(parameterIndex, x)
        paramMsg.add("${formatter.toString(x)}(NString)")
    }

    override fun setNCharacterStream(parameterIndex: Int, value: Reader, length: Long) {
        stmt.setNCharacterStream(parameterIndex, value, length)
        paramMsg.add("${formatter.toString(value)}(NCharacterStream)")
    }

    override fun setNCharacterStream(parameterIndex: Int, value: Reader) {
        stmt.setNCharacterStream(parameterIndex, value)
        paramMsg.add("${formatter.toString(value)}(NCharacterStream)")
    }

    override fun setNClob(parameterIndex: Int, reader: Reader) {
        stmt.setNClob(parameterIndex, reader)
        paramMsg.add("${formatter.toString(reader)}(NClob)")
    }

    override fun setNClob(parameterIndex: Int, reader: Reader, length: Long) {
        stmt.setNClob(parameterIndex, reader, length)
        paramMsg.add("${formatter.toString(reader)}(NClob)")
    }

    override fun setNClob(parameterIndex: Int, value: NClob) {
        stmt.setNClob(parameterIndex, value)
        paramMsg.add("${formatter.toString(value)}(NClob)")
    }

    override fun setSQLXML(parameterIndex: Int, xmlObject: SQLXML) {
        stmt.setSQLXML(parameterIndex, xmlObject)
        paramMsg.add("${formatter.toString(xmlObject)}(SQLXML)")
    }

    override fun close() {
        stmt.close()
        closeListener?.invoke()
    }
}